from machine import reset, Pin
from time import sleep
import usocket as soc
import gc
import network
import camera

# -------------------------- 1. 核心配置常量 --------------------------
# 摄像头配置常量
PIN_PWDN    = const(0)   # 电源使能引脚
PIN_RESET   = const(1)   # 复位引脚
PIN_XCLK    = const(2)   # 时钟引脚
PIN_SIOD    = const(3)   # I2C SDA引脚
PIN_SIOC    = const(4)   # I2C SCL引脚
PIN_D7      = const(5)   # 图像数据引脚D7
PIN_D6      = const(6)   # 图像数据引脚D6
PIN_D5      = const(7)   # 图像数据引脚D5
PIN_D4      = const(8)   # 图像数据引脚D4
PIN_D3      = const(9)   # 图像数据引脚D3
PIN_D2      = const(10)  # 图像数据引脚D2
PIN_D1      = const(11)  # 图像数据引脚D1
PIN_D0      = const(12)  # 图像数据引脚D0
PIN_VSYNC   = const(13)  # 场同步引脚
PIN_HREF    = const(14)  # 行同步引脚
PIN_PCLK    = const(15)  # 像素时钟引脚
XCLK_MHZ    = const(16)  # XCLK时钟频率
PIXFORMAT   = const(17)  # 像素格式
FRAMESIZE   = const(18)  # 帧大小
JPEG_QUALITY= const(19)  # JPEG质量
FB_COUNT    = const(20)  # 帧缓冲数量

# AI-Thinker ESP32-CAM 官方引脚映射
ai_thinker = {
    PIN_PWDN:32,
    PIN_RESET:-1,  # 官方用-1，若初始化失败，改为15
    PIN_XCLK:0,
    PIN_SIOD:26,
    PIN_SIOC:27,
    PIN_D7:35,
    PIN_D6:34,
    PIN_D5:39,
    PIN_D4:36,
    PIN_D3:21,
    PIN_D2:19,
    PIN_D1:18,
    PIN_D0:5,
    PIN_VSYNC:25,
    PIN_HREF:23,
    PIN_PCLK:22,
    XCLK_MHZ:16,
    PIXFORMAT:5,       # PIXFORMAT_JPEG
    FRAMESIZE:10,      # FRAMESIZE_SVGA (800x600)
    JPEG_QUALITY:10,
    FB_COUNT:1, 
}

# -------------------------- 2. HTML与HTTP头 --------------------------
pg = {
    'foto':'''
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>六足机器人上位机</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <link rel="icon" href="">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#165DFF',
                        secondary: '#36D399',
                        accent: '#FF6B35',
                        dark: '#1E293B',
                        light: '#F8FAFC',
                        frame: '#E2E8F0',
                        control: '#FF5722',
                        radar: '#00FF00',
                    },
                    fontFamily: {
                        inter: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto { content-visibility: auto; }
            .grid-layout { grid-template-rows: 65% 35%; height: 100%; }
            .grid-layout-right { grid-template-rows: 50% 50%; height: 100%; }
            .frame-border { border: 5px solid theme('colors.frame'); }
            .app-height { height: calc(100vh); }
            .key-button { @apply w-12 h-12 flex items-center justify-center rounded-lg bg-gray-100 text-gray-700 font-medium transition-all duration-200 shadow-sm; }
            .key-button-active { @apply bg-control text-white transform scale-105 shadow-md; }
            .robot-arena { @apply relative w-full h-full bg-gray-100 rounded-lg overflow-hidden; }
            .robot { @apply absolute w-12 h-12 bg-control rounded-full flex items-center justify-center text-white transition-all duration-150 shadow-md; }
            .keyboard-container { @apply grid grid-cols-3 gap-4 justify-center max-w-[12rem] mx-auto; }
            .video-container { @apply relative w-full h-full bg-gray-100 rounded-lg overflow-hidden; }
            .video-placeholder { @apply absolute inset-0 flex flex-col items-center justify-center bg-gray-100; }
            .sensor-data-container { @apply w-full h-full bg-gray-50 rounded-lg overflow-hidden p-4; }
            .sensor-grid { @apply grid grid-cols-2 gap-2 h-full; }
            .sensor-item { @apply bg-white p-2 h-full rounded-lg shadow-sm flex flex-col justify-center; }
            .sensor-label { @apply text-base text-gray-500 mb-1; }
            .sensor-value { @apply text-base font-semibold text-dark; }
            .radar-container { @apply w-full h-full bg-dark rounded-lg overflow-hidden relative; }
        }
    </style>
</head>
<body class="font-inter bg-gray-50">
    <div class="relative app-height frame-border overflow-hidden w-full">
        <div class="absolute left-1/2 top-0 bottom-0 w-[5px] bg-frame -ml-[2.5px] z-10"></div>

        <!-- 左侧区域：摄像头+控制 -->
        <div class="absolute left-0 top-0 bottom-0 w-1/2 grid grid-layout">
            <div class="absolute left-0 right-0 top-[65%] h-[5px] bg-frame -mt-[2.5px] z-10"></div>

            <!-- 左上：摄像头视频 -->
            <div class="bg-white p-4 overflow-hidden relative">
                <div class="absolute inset-0 bg-primary/5 z-0"></div>
                <div class="relative z-10">
                    <h2 class="text-xl font-bold text-dark mb-3 flex items-center"><i class="fa fa-area-chart text-primary mr-2"></i>摄像头</h2>
                    <div class="video-container">
                        <div id="video-placeholder" class="video-placeholder">
                            <div class="animate-pulse rounded-full h-16 w-16 bg-gray-300 mb-3"></div>
                            <div class="animate-pulse rounded h-16 w-3/4 bg-gray-300 mb-2"></div>
                            <div class="animate-pulse rounded h-16 w-1/2 bg-gray-300"></div>
                            <p class="mt-4 text-gray-500 text-sm" id="video-status-text">正在连接ESP32-CAM...</p>
                        </div>
                        <img id="esp32-cam-video" src="/live" alt="ESP32-CAM视频流" class="w-full h-80 object-cover hidden">
                        <div class="absolute bottom-3 right-3 flex gap-2 z-20">
                            <button id="refresh-video" class="bg-white/80 hover:bg-white text-primary p-2 rounded-full shadow-md transition-colors"><i class="fa fa-refresh"></i></button>
                            <button id="fullscreen-video" class="bg-white/80 hover:bg-white text-primary p-2 rounded-full shadow-md transition-colors"><i class="fa fa-expand"></i></button>
                            <button id="exit-fullscreen" class="bg-white/80 hover:bg-white text-primary p-2 rounded-full shadow-md transition-colors hidden"><i class="fa fa-compress"></i></button>
                        </div>
                    </div>
                    <div class="mt-2 flex justify-between items-center">
                        <div class="text-xs text-gray-500"><span id="video-status">状态: <span id="video-status-detail">连接中...</span></span></div>
                    </div>
                </div>
                <div class="absolute -bottom-3 -right-3 w-16 h-16 bg-primary/10 rounded-full"></div>
            </div>

            <!-- 左下：机器人控制 -->
            <div class="bg-white p-4 overflow-hidden relative">
                <div class="absolute inset-0 bg-control/5 z-0"></div>
                <div class="relative z-10">
                    <h2 class="text-lg font-bold text-dark mb-2 flex items-center"><i class="fa fa-gamepad text-control mr-2"></i>机器人控制</h2>
                    <div class="absolute top-6 right-4 flex flex-col gap-2 z-20">
                        <div class="flex gap-2">
                            <button id="gait-wave" class="px-2 py-1 rounded-md text-base transition-all duration-200 bg-gray-200 text-gray-700 hover:bg-gray-300">Wave</button>
                            <button id="gait-triangle" class="px-2 py-1 rounded-md text-base transition-all duration-200 bg-control text-white">Triangle</button>
                        </div>
                        <div class="ml-[calc(44px+20px)]">
                            <button id="camera-light" class="px-2 py-1 rounded-md text-base transition-all duration-200 bg-gray-200 text-gray-700 hover:bg-gray-300">light</button>
                        </div>
                    </div>
                    <div class="keyboard-container mt-8">
                        <div class="col-span-3 flex justify-center"><button id="btn-up" class="key-button text-base">W</button></div>
                        <div class="col-span-3 grid grid-cols-3 gap-2">
                            <div class="col-start-1 flex justify-end"><button id="btn-left" class="key-button text-base">A</button></div>
                            <div class="flex justify-center"><button id="btn-down" class="key-button text-base">S</button></div>
                            <div class="flex justify-start"><button id="btn-right" class="key-button text-base">D</button></div>
                        </div>
                    </div>
                    <div class="robot-arena"><div id="robot" class="robot"><i class="fa fa-arrow-up"></i></div></div>
                    <div class="mt-16 text-base text-gray-500 flex flex-wrap gap-x-5">
                        <div class="w-full sm:w-auto"><span>当前指令:</span> <span id="current-command">等待输入...</span></div>
                        <div class="w-full sm:w-auto"><span>步态模式:</span> <span id="gait-mode">Triangle</span></div>
                        <div class="w-full sm:w-auto"><span>补光灯:</span> <span id="camera-light-status">关</span></div>
                    </div>
                </div>
                <div class="absolute -bottom-3 -right-3 w-12 h-12 bg-control/10 rounded-full"></div>
            </div>
        </div>

        <!-- 右侧区域：传感器+雷达 -->
        <div class="absolute right-0 top-0 bottom-0 w-1/2 grid grid-layout-right">
            <div class="absolute left-0 right-0 top-1/2 h-[5px] bg-frame -mt-[2.5px] z-10"></div>

            <!-- 右上：传感器数据 -->
            <div class="bg-white p-4 overflow-hidden relative">
                <div class="absolute inset-0 bg-accent/5 z-0"></div>
                <div class="relative z-10">
                    <h2 class="text-lg font-bold text-dark mb-2 flex items-center"><i class="fa fa-tachometer text-accent mr-2"></i>传感器数据</h2>
                    <div class="sensor-data-container">
                        <div class="sensor-grid">
                            <div class="sensor-item"><span class="sensor-label">环境温度</span><span class="sensor-value" id="temp-value">25.6°C</span></div>
                            <div class="sensor-item"><span class="sensor-label">环境湿度</span><span class="sensor-value" id="humidity-value">48.2%</span></div>
                            <div class="sensor-item"><span class="sensor-label">纬度</span><span class="sensor-value" id="latitude-value">30.1234°</span></div>
                            <div class="sensor-item"><span class="sensor-label">经度</span><span class="sensor-value" id="longitude-value">120.5678°</span></div>
                            <div class="sensor-item"><span class="sensor-label">海拔高度</span><span class="sensor-value" id="altitude">130M</span></div>
                            <div class="sensor-item"><span class="sensor-label">电池电压</span><span class="sensor-value" id="battery-voltage">7.8V</span></div>
                            <div class="sensor-item"><span class="sensor-label">电池温度</span><span class="sensor-value" id="battery-temp">32.1°C</span></div>
                            <div class="sensor-item"><span class="sensor-label">开机时间</span><span class="sensor-value text-green-500" id="uptime-value">02:35:42</span></div>
                        </div>
                    </div>
                </div>
                <div class="absolute -top-3 -right-3 w-12 h-12 bg-accent/10 rounded-full"></div>
            </div>

            <!-- 右下：雷达图 -->
            <div class="bg-white p-4 overflow-hidden relative">
                <div class="absolute inset-0 bg-dark/5 z-0"></div>
                <div class="relative z-10">
                    <h2 class="text-lg font-bold text-dark mb-2 flex items-center"><i class="fa fa-rss text-dark mr-2"></i>360°雷达点云图</h2>
                    <div class="radar-container" id="radar-container">
                        <canvas id="radar-canvas" width="400" height="350"></canvas>
                        <div class="absolute bottom-2 left-2 text-xs text-white/80"><span id="radar-status">状态: 初始状态</span></div>
                    </div>
                </div>
                <div class="absolute -bottom-3 -left-3 w-12 h-12 bg-dark/10 rounded-full"></div>
            </div>
        </div>
    </div>

    <script>
        const esp32Data = { isConnected: false, videoStream: null, radarPoints: [], robotStatus: { position: { x: 50, y: 50 }, direction: 0, gait: 'triangle', radarAngle: -Math.PI / 2 } };
        let isFullscreen = false, radarCanvas, radarCtx, radarRotation = 0, radarZoom = 1, radarCenterX = 200, radarCenterY = 150, radarRunning = true;

        document.addEventListener('DOMContentLoaded', () => {
            initVideoStream();
            initRobotControl();
            setTimeout(initRadar, 100);
            initSensorDataSimulation();
            window.addEventListener('resize', () => radarCanvas && resizeRadarCanvas());
        });

        // 视频流处理
        function initVideoStream() {
            const videoEl = document.getElementById('esp32-cam-video'), placeholder = document.getElementById('video-placeholder');
            const refreshBtn = document.getElementById('refresh-video'), fullscreenBtn = document.getElementById('fullscreen-video');
            const exitFullBtn = document.getElementById('exit-fullscreen'), statusDetail = document.getElementById('video-status-detail');
            const statusText = document.getElementById('video-status-text');

            // 模拟连接（实际需依赖ESP32的/live接口）
            setTimeout(() => {
                const success = Math.random() < 0.8; // 提高连接成功率
                if (success) {
                    placeholder.classList.add('hidden');
                    videoEl.classList.remove('hidden');
                    statusDetail.textContent = '连接成功';
                    statusText.textContent = 'ESP32-CAM已连接';
                    esp32Data.isConnected = true;
                } else {
                    statusDetail.textContent = '连接失败';
                    statusText.textContent = '连接超时，请点击刷新重试';
                    statusText.classList.add('text-red-500');
                }
            }, 3000);

            // 刷新视频
            refreshBtn.addEventListener('click', () => {
                videoEl.classList.add('hidden');
                placeholder.classList.remove('hidden');
                statusDetail.textContent = '重新连接...';
                statusText.textContent = '正在重新连接...';
                statusText.classList.remove('text-red-500');
                setTimeout(() => {
                    const success = Math.random() > 0.2;
                    if (success) {
                        placeholder.classList.add('hidden');
                        videoEl.classList.remove('hidden');
                        statusDetail.textContent = '连接成功';
                        statusText.textContent = 'ESP32-CAM已连接';
                        esp32Data.isConnected = true;
                    } else {
                        statusDetail.textContent = '连接失败';
                        statusText.textContent = '重新连接超时，请重试';
                        statusText.classList.add('text-red-500');
                    }
                }, 3000);
            });

            // 全屏控制
            fullscreenBtn.addEventListener('click', () => requestFullscreen(document.querySelector('.video-container')));
            exitFullBtn.addEventListener('click', exitFullscreen);
            ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'].forEach(evt => {
                document.addEventListener(evt, () => {
                    isFullscreen = !!document.fullscreenElement;
                    fullscreenBtn.classList.toggle('hidden', isFullscreen);
                    exitFullBtn.classList.toggle('hidden', !isFullscreen);
                });
            });
        }

        // 全屏工具函数
        function requestFullscreen(el) {
            try {
                el.requestFullscreen?.() || el.webkitRequestFullscreen?.() || el.msRequestFullscreen?.() || el.mozRequestFullScreen?.();
            } catch (e) { alert('全屏失败: ' + e.message); }
        }
        function exitFullscreen() {
            try {
                document.exitFullscreen?.() || document.webkitExitFullscreen?.() || document.msExitFullscreen?.() || document.mozCancelFullScreen?.();
            } catch (e) { alert('退出全屏失败: ' + e.message); }
        }

        // 机器人控制
        function initRobotControl() {
            const robot = {
                el: document.getElementById('robot'),
                cmdEl: document.getElementById('current-command'),
                gaitEl: document.getElementById('gait-mode'),
                lightEl: document.getElementById('camera-light-status'),
                update() {
                    this.el.style.left = `${esp32Data.robotStatus.position.x}%`;
                    this.el.style.top = `${esp32Data.robotStatus.position.y}%`;
                    this.el.style.transform = `rotate(${esp32Data.robotStatus.direction * 90}deg)`;
                    this.gaitEl.textContent = esp32Data.robotStatus.gait.charAt(0).toUpperCase() + esp32Data.robotStatus.gait.slice(1);
                    this.lightEl.textContent = esp32Data.robotStatus.lightOn ? '开' : '关';
                }
            };
            esp32Data.robotStatus.lightOn ??= false; // 初始化补光灯状态

            // 移动逻辑
            function move(direction) {
                const speed = 5, arena = document.querySelector('.robot-arena');
                const arenaRect = arena.getBoundingClientRect(), robotSize = robot.el.offsetWidth;
                const maxX = 100 - (robotSize / arenaRect.width * 100), maxY = 100 - (robotSize / arenaRect.height * 100);
                const { x, y } = esp32Data.robotStatus.position;

                switch(direction) {
                    case 'up': esp32Data.robotStatus.position.y = Math.max(0, y - speed); esp32Data.robotStatus.direction = 0; break;
                    case 'down': esp32Data.robotStatus.position.y = Math.min(maxY, y + speed); esp32Data.robotStatus.direction = 2; break;
                    case 'left': esp32Data.robotStatus.position.x = Math.max(0, x - speed); esp32Data.robotStatus.direction = 3; break;
                    case 'right': esp32Data.robotStatus.position.x = Math.min(maxX, x + speed); esp32Data.robotStatus.direction = 1; break;
                }
                robot.update();
                robot.cmdEl.textContent = direction;
            }

            // 步态切换
            function switchGait(gait) {
                document.querySelectorAll('#gait-wave, #gait-triangle').forEach(btn => {
                    btn.classList.toggle('bg-control', btn.id === `gait-${gait}`);
                    btn.classList.toggle('text-white', btn.id === `gait-${gait}`);
                    btn.classList.toggle('bg-gray-200', btn.id !== `gait-${gait}`);
                    btn.classList.toggle('text-gray-700', btn.id !== `gait-${gait}`);
                });
                esp32Data.robotStatus.gait = gait;
                robot.update();
            }

            // 补光灯切换
            function toggleLight() {
                const btn = document.getElementById('camera-light');
                esp32Data.robotStatus.lightOn = !esp32Data.robotStatus.lightOn;
                btn.classList.toggle('bg-control', esp32Data.robotStatus.lightOn);
                btn.classList.toggle('text-white', esp32Data.robotStatus.lightOn);
                btn.classList.toggle('bg-gray-200', !esp32Data.robotStatus.lightOn);
                btn.classList.toggle('text-gray-700', !esp32Data.robotStatus.lightOn);
                robot.update();
            }

            // 按键绑定（鼠标+键盘）
            const keyMap = { 'w':'up', 's':'down', 'a':'left', 'd':'right' };
            Object.entries(keyMap).forEach(([key, dir]) => {
                document.getElementById(`btn-${dir}`).addEventListener('mousedown', () => {
                    document.getElementById(`btn-${dir}`).classList.add('key-button-active');
                    move(dir);
                });
                document.addEventListener('keydown', (e) => {
                    if (e.key.toLowerCase() === key) {
                        e.preventDefault();
                        document.getElementById(`btn-${dir}`).classList.add('key-button-active');
                        move(dir);
                    }
                });
            });

            // 按键释放
            document.addEventListener('mouseup', () => Object.values(keyMap).forEach(dir => {
                document.getElementById(`btn-${dir}`).classList.remove('key-button-active');
            }));
            document.addEventListener('keyup', (e) => {
                if (Object.keys(keyMap).includes(e.key.toLowerCase())) {
                    document.getElementById(`btn-${keyMap[e.key.toLowerCase()]}`).classList.remove('key-button-active');
                }
            });

            // 步态+灯光绑定
            document.getElementById('gait-wave').addEventListener('click', () => switchGait('wave'));
            document.getElementById('gait-triangle').addEventListener('click', () => switchGait('triangle'));
            document.getElementById('camera-light').addEventListener('click', toggleLight);

            robot.update(); // 初始化显示
        }

        // 传感器数据模拟
        function initSensorDataSimulation() {
            let temp=25.6, humidity=48.2, batVolt=7.8, batTemp=32.1, uptime=9342, altitude=130;
            setInterval(() => {
                // 温度波动
                temp += (Math.random()-0.5)*0.4; temp = Math.max(20, Math.min(35, temp));
                document.getElementById('temp-value').textContent = temp.toFixed(1) + '°C';
                // 湿度波动
                humidity += (Math.random()-0.5)*1.2; humidity = Math.max(30, Math.min(70, humidity));
                document.getElementById('humidity-value').textContent = humidity.toFixed(1) + '%';
                // 电池电压（缓慢下降）
                batVolt -= 0.001; batVolt = Math.max(7.0, batVolt);
                document.getElementById('battery-voltage').textContent = batVolt.toFixed(1) + 'V';
                // 电池温度波动
                batTemp += (Math.random()-0.5)*0.6; batTemp = Math.max(30, Math.min(40, batTemp));
                document.getElementById('battery-temp').textContent = batTemp.toFixed(1) + '°C';
                // 开机时间
                uptime += 3;
                const h = Math.floor(uptime/3600).toString().padStart(2,'0');
                const m = Math.floor((uptime%3600)/60).toString().padStart(2,'0');
                const s = (uptime%60).toString().padStart(2,'0');
                document.getElementById('uptime-value').textContent = `${h}:${m}:${s}`;
                // 海拔波动
                altitude += (Math.random()-0.5)*0.4; altitude = Math.max(120, Math.min(140, altitude));
                document.getElementById('altitude').textContent = altitude.toFixed(1) + 'M';
            }, 3000);
        }

        // 雷达图逻辑
        function initRadar() {
            radarCanvas = document.getElementById('radar-canvas');
            radarCtx = radarCanvas.getContext('2d');
            resizeRadarCanvas();
            document.getElementById('radar-status').textContent = '状态: 扫描中...';
            animateRadar();
        }
        function resizeRadarCanvas() {
            const container = document.getElementById('radar-container');
            radarCanvas.width = container.clientWidth;
            radarCanvas.height = container.clientHeight;
            radarCenterX = radarCanvas.width/2;
            radarCenterY = radarCanvas.height/2;
            drawRadar();
        }
        function drawRadar() {
            radarCtx.clearRect(0,0,radarCanvas.width,radarCanvas.height);
            drawRadarBg();
            drawRadarPoints();
            radarRunning ? drawDynamicScan() : drawStaticScan();
        }
        function animateRadar() {
            if (radarRunning) {
                radarCtx.clearRect(0,0,radarCanvas.width,radarCanvas.height);
                drawRadarBg();
                drawRadarPoints();
                drawDynamicScan();
                radarRotation += 0.03;
                if (radarRotation > Math.PI*2) radarRotation -= Math.PI*2;
                requestAnimationFrame(animateRadar);
            }
        }
        function drawRadarBg() {
            // 黑色背景
            radarCtx.fillStyle = '#000';
            radarCtx.fillRect(0,0,radarCanvas.width,radarCanvas.height);
            // 同心圆
            const maxRadius = Math.min(radarCanvas.width, radarCanvas.height)*0.45;
            radarCtx.strokeStyle = '#fff';
            radarCtx.lineWidth = 2;
            for (let i=1; i<=5; i++) {
                const r = (maxRadius/5)*i;
                radarCtx.beginPath();
                radarCtx.arc(radarCenterX, radarCenterY, r/radarZoom, 0, Math.PI*2);
                radarCtx.stroke();
            }
            // 十字线
            radarCtx.beginPath();
            radarCtx.moveTo(radarCenterX - maxRadius/radarZoom, radarCenterY);
            radarCtx.lineTo(radarCenterX + maxRadius/radarZoom, radarCenterY);
            radarCtx.moveTo(radarCenterX, radarCenterY - maxRadius/radarZoom);
            radarCtx.lineTo(radarCenterX, radarCenterY + maxRadius/radarZoom);
            radarCtx.stroke();
            // 距离标记
            radarCtx.fillStyle = '#fff';
            radarCtx.font = '10px monospace';
            for (let i=1; i<=5; i++) {
                const r = (maxRadius/5)*i;
                const text = `${i*50}`;
                const textW = radarCtx.measureText(text).width;
                radarCtx.fillText(text, radarCenterX + r/radarZoom - textW -5, radarCenterY -5);
            }
        }
        function drawRadarPoints() { /* 预留：实际项目可添加障碍物点云逻辑 */ }
        function drawStaticScan() {
            const maxRadius = Math.min(radarCanvas.width, radarCanvas.height)*0.45;
            const angle = Math.PI/4;
            // 扫描线
            radarCtx.strokeStyle = '#fff';
            radarCtx.lineWidth = 2;
            radarCtx.beginPath();
            radarCtx.moveTo(radarCenterX, radarCenterY);
            radarCtx.lineTo(radarCenterX + Math.cos(angle)*maxRadius/radarZoom, radarCenterY + Math.sin(angle)*maxRadius/radarZoom);
            radarCtx.stroke();
            // 扇形渐变
            const grad = radarCtx.createRadialGradient(radarCenterX, radarCenterY, 0, radarCenterX, radarCenterY, maxRadius/radarZoom);
            grad.addColorStop(0, 'rgba(0,255,0,0.05)');
            grad.addColorStop(1, 'rgba(0,255,0,0)');
            radarCtx.fillStyle = grad;
            radarCtx.beginPath();
            radarCtx.moveTo(radarCenterX, radarCenterY);
            radarCtx.arc(radarCenterX, radarCenterY, maxRadius/radarZoom, angle-0.3, angle);
            radarCtx.closePath();
            radarCtx.fill();
        }
        function drawDynamicScan() {
            const maxRadius = Math.min(radarCanvas.width, radarCanvas.height)*0.45;
            // 扫描线
            radarCtx.strokeStyle = '#fff';
            radarCtx.lineWidth = 2;
            radarCtx.beginPath();
            radarCtx.moveTo(radarCenterX, radarCenterY);
            radarCtx.lineTo(radarCenterX + Math.cos(radarRotation)*maxRadius/radarZoom, radarCenterY + Math.sin(radarRotation)*maxRadius/radarZoom);
            radarCtx.stroke();
            // 扇形渐变
            const grad = radarCtx.createRadialGradient(radarCenterX, radarCenterY, 0, radarCenterX, radarCenterY, maxRadius/radarZoom);
            grad.addColorStop(0, 'rgba(0,255,0,0.05)');
            grad.addColorStop(1, 'rgba(0,255,0,0)');
            radarCtx.fillStyle = grad;
            radarCtx.beginPath();
            radarCtx.moveTo(radarCenterX, radarCenterY);
            radarCtx.arc(radarCenterX, radarCenterY, maxRadius/radarZoom, radarRotation-0.3, radarRotation);
            radarCtx.closePath();
            radarCtx.fill();
        }
    </script>
</body>
</html>
''',
}

hdr = {
    'foto': """HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Connection: Closed
Content-Length: %d""",
    'stream': """HTTP/1.1 200 OK
Content-Type: multipart/x-mixed-replace; boundary=frame
Connection: keep-alive
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Expires: Thu, Jan 01 1970 00:00:00 GMT
Pragma: no-cache""",
    'frame': """--frame
Content-Type: image/jpeg""",
}

# -------------------------- 3. WiFi配置（优化连接稳定性） --------------------------
class Sta:
    AP = "黑了心的蛆"  # 替换为你的WiFi名称
    PWD = "19234949992.ch"       # 替换为你的WiFi密码

    def __init__(self):
        self.wlan = network.WLAN(network.STA_IF)
        self.wlan.active(True)
    def connect(self):
        self.wlan.connect(self.AP, self.PWD)
        self.wlan.config(pm=0xa11140)  # 禁用省电
    def wait(self):
        while not self.wlan.isconnected():
            print("等待连接...", self.wlan.status())
            sleep(1)
        return self.wlan.ifconfig()[0]

# -------------------------- 4. 服务器与路由 --------------------------
ip = ''
camera_instance = None

def clean_up(cs):
    '''安全关闭socket，释放内存'''
    try:
        cs.close()
    except Exception as e:
        print(f"关闭socket时出错: {e}")
    finally:
        del cs
        gc.collect()  # 主动回收内存

def handle_root(cs):
    '''处理根路径请求（返回HTML页面）'''
    html = pg['foto']
    html_len = len(html.encode('utf-8'))
    response = hdr['foto'] % html_len + '\r\n\r\n' + html + '\r\n'
    cs.write(response.encode('utf-8'))

def handle_live(cs):
    '''处理视频流请求（/live路径）'''
    global camera_instance
    # 发送视频流响应头
    cs.write(hdr['stream'].encode('utf-8') + b'\r\n\r\n')
    cs.setblocking(True)
    try:
        while True:
            # 捕获一帧图像
            frame = camera_instance.capture()
            if not frame:
                print("捕获图像失败，重试...")
                sleep(0.1)
                continue
            # 发送帧数据
            cs.write(hdr['frame'].encode('utf-8') + b'\r\n')
            cs.write(f"Content-Length: {len(frame)}\r\n\r\n".encode('utf-8'))
            cs.write(frame)
            cs.write(b'\r\n\r\n')
            sleep(0.05)  # 控制帧率
    except Exception as e:
        print(f"视频流错误: {e}")
    finally:
        clean_up(cs)

def route(cs, path):
    '''路由分发'''
    print(f"收到请求: {path}")
    if path in ('/', '/webcam'):
        handle_root(cs)
    elif path == '/live':
        handle_live(cs)
    else:
        print(f"未知路径: {path}，忽略")
    clean_up(cs)

def start_server(port=80):
    '''启动Web服务器'''
    global ip
    # 创建TCP socket
    server_sock = soc.socket(soc.AF_INET, soc.SOCK_STREAM)
    server_sock.setsockopt(soc.SOL_SOCKET, soc.SO_REUSEADDR, 1)
    server_sock.bind(('0.0.0.0', port))
    server_sock.listen(1)
    print(f"服务器已启动 | 访问地址: http://{ip} | 端口: {port}")

    while True:
        try:
            client_sock, client_addr = server_sock.accept()
            client_sock.settimeout(10.0)
            # 读取请求头
            request = client_sock.recv(1024).decode('utf-8', errors='ignore')
            if not request:
                clean_up(client_sock)
                continue
            # 解析请求路径
            request_lines = request.split('\n')
            if not request_lines:
                clean_up(client_sock)
                continue
            path = request_lines[0].split(' ')[1] if len(request_lines[0].split(' '))>=2 else '/'
            # 分发请求
            route(client_sock, path)
        except Exception as e:
            print(f"服务器异常: {e}")
            if 'client_sock' in locals():
                clean_up(client_sock)

# -------------------------- 5. 主函数 --------------------------
def main():
    global ip, camera_instance
    gc.enable()
    gc.collect()

    # 连接WiFi
    print("=== 第一步：连接WiFi ===")
    wifi = Sta()
    wifi.connect()
    ip = wifi.wait()
    if not ip:
        print("WiFi连接失败，10秒后重启设备...")
        sleep(10)
        reset()

    # 初始化摄像头
    print("\n=== 第二步：初始化摄像头 ===")
    # 初始化PWDN引脚
    pin_pwdn = Pin(ai_thinker[PIN_PWDN], Pin.OUT)
    # 摄像头硬件复位
    print("执行摄像头上电时序...")
    pin_pwdn.value(0)  # 拉低PWDN，摄像头断电
    sleep(0.2)
    pin_pwdn.value(1)  # 拉高PWDN，摄像头上电
    sleep(0.5)

    # 配置摄像头引脚
    def configure_camera(cam, config):
        for key, val in config.items():
            cam.conf(key, val)
            sleep(0.01)

    configure_camera(camera, ai_thinker)
    # 额外配置
    camera.conf(XCLK_MHZ, 16)
    camera.framesize(ai_thinker[FRAMESIZE])
    camera.quality(ai_thinker[JPEG_QUALITY])
    camera.brightness(0)
    camera.contrast(0)
    camera.flip(0)

    # 多次尝试初始化摄像头
    cam_ready = False
    for i in range(5):
        print(f"初始化摄像头（尝试{i+1}/5）...")
        cam_ready = camera.init()
        if cam_ready:
            print("摄像头初始化成功！")
            break
        else:
            print(f"第{i+1}次初始化失败，重试...")
            # 重试前重新执行上电时序
            pin_pwdn.value(0)
            sleep(0.2)
            pin_pwdn.value(1)
            sleep(0.5)

    # 初始化失败处理
    if not cam_ready:
        print("摄像头初始化失败，10秒后重启设备...")
        sleep(10)
        reset()

    # 保存摄像头实例
    camera_instance = camera
    print(f"摄像头配置：帧大小={camera.framesize()}, 格式={camera.pixformat()}, 质量={camera.quality()}")

    # 启动Web服务器
    print("\n=== 第三步：启动Web服务器 ===")
    start_server(port=80)

# -------------------------- 启动主函数 --------------------------
if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(f"主程序异常: {e}")
        sleep(5)
        reset()

